﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ServiceModel.Channels;
using System.ServiceModel;
using System.IdentityModel.Policy;
using System.IdentityModel.Claims;
using System.IdentityModel.Tokens;
using System.Xml;

namespace PSE.ProcessManager.Common.Gateway
{
    /// <summary>
    /// Classe responsável na criação de uma mensagem WCF.
    /// </summary>
    public class MessageCreator
    {
        /// <summary>
        /// Claims que deverão ser ignoradas na criação do header com os dados de autorizacao
        /// </summary>
        private static string[] _claimsToIgnore = { "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/hash", 
                                                    "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/nameidentifier",
                                                    "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/nameidentifier",
                                                    "http://schemas.microsoft.com/xsi/2005/05/role/role"};


        /// <summary>
        /// Cria o cabeçalho da mensagem.
        /// </summary>
        /// <param name="message">Message contendo o Id da Mensagem, a operação e o Id do processo.</param>
        /// <returns>Uma nova instância do MessageHeader contendo um ProcessMessageHeader</returns>
        protected static List<MessageHeader> CreateMessageHeader(BaseMessage message)
        {
            List<MessageHeader> headerList = new List<MessageHeader>();

            ///Criar o Header padrão da mensagem
            ProcessMessageHeader header = new ProcessMessageHeader();
            header.MessageId = message.MessageId;
            header.Operation = message.Operation;
            header.ProcessId = message.ProcessId;
            header.From = message.From;
            header.To = message.To;
            headerList.Add(MessageHeader.CreateHeader(ProcessMessageHeader.Name, ProcessMessageHeader.NameSpace, header));

            //OBS: Foi feito dessa forma pois caso contrario seria necessario especializar o MessageCreator para o Gateway do processo
            //Tentar criar o header com os dados do contexto de autorizacao
            ClaimsMessageHeader athzHeader = CreateAuthorizationHeader();
            if (athzHeader != null)
                headerList.Add(MessageHeader.CreateHeader(ClaimsMessageHeader.Name, ClaimsMessageHeader.NameSpace, athzHeader));

            return headerList;
        }

        /// <summary>
        /// Criar, quando possível o header com os dados de authorizacao, 
        /// a partir dos dados contidos do AuthorizarionContext do contexto de operação
        /// Caso o contexto de operação não exista, não será criado o header
        /// </summary>
        /// <returns>Header com os dados de authorização</returns>
        private static ClaimsMessageHeader CreateAuthorizationHeader()
        {
            ClaimsMessageHeader header = null;

            //tentar recuperar o contexto de operação
            OperationContext operationContext = OperationContext.Current;

            //caso exista um contexto (mensagem sendo criada no server)
            if (operationContext != null && operationContext.ServiceSecurityContext != null)
            {
                AuthorizationContext athzContext = operationContext.ServiceSecurityContext.AuthorizationContext;
                
                if (athzContext != null &&
                    // Processos iniciados a partir da interface de gerenciamento tem PrimaryIdentity como WindowsIdentity 
                    // e não devem adicionar informações no header
                    operationContext.ServiceSecurityContext.PrimaryIdentity is System.Security.Principal.GenericIdentity)
                {
                    header = new ClaimsMessageHeader();
                    header.Data = new Dictionary<string, object>();

                    foreach (ClaimSet cs in athzContext.ClaimSets)
                    {
                        for (int i = 0; i < cs.Count; i++)
                        {
                            Claim c = cs[i];

                            //se a claim estiver na lista de claims a ignorar, não adiciona-la ao dicionario
                            if (!_claimsToIgnore.Contains<string>(c.ClaimType))
                            {
                                //se a claim já tiver sido incluida, ignorar
                                if (!header.Data.ContainsKey(c.ClaimType))
                                {
                                    header.Data.Add(c.ClaimType, c.Resource);
                                }
                            }
                        }
                    }
                }
            }

            return header;
        }

        /// <summary>
        /// Cria uma mensagem WCF para envio.
        /// </summary>
        /// <param name="messageVersion">Versão da mensagem.</param>
        /// <param name="message">Mensagem contendo as informações necessárias.</param>
        /// <returns>Channels.Message</returns>
        public static System.ServiceModel.Channels.Message CreateMessage(PSE.ProcessManager.Common.Gateway.BaseMessage message, string action)
        {
            if (message is IMessage)
            {
                return CreateMessage((IMessage)message, action);
            }
            else
            {
                ////Cria o cabeçalho da mensagem.
                List<MessageHeader> headerList = CreateMessageHeader(message);
                System.ServiceModel.Channels.Message msg = null;
                

                //Cria uma cópia da mensagem original, para repassar o body para frente.
                if (message.Original != null)
                {
                    msg = message.Original.CreateMessage();

                    if (!msg.IsEmpty)
                    {
                        msg = System.ServiceModel.Channels.Message.CreateMessage
                                                                        (MessageVersion.Soap12WSAddressing10,
                                                                         action,
                                                                         msg.GetReaderAtBodyContents());
                    }
                    else
                        msg = System.ServiceModel.Channels.Message.CreateMessage(MessageVersion.Soap12WSAddressing10, action);
                }
                else
                    msg = System.ServiceModel.Channels.Message.CreateMessage(MessageVersion.Soap12WSAddressing10, action);

                //adicionar os header criados a mensagem
                foreach (MessageHeader header in headerList)
                    msg.Headers.Add(header);

                return msg;
            }
        }


        public static System.ServiceModel.Channels.Message CreateMessage(IMessage message, string action)
        {
            //Cria o cabeçalho da mensagem.
            List<MessageHeader> headerList = CreateMessageHeader((BaseMessage)message);

            System.ServiceModel.Channels.Message msg = System.ServiceModel.Channels.Message.CreateMessage
                                                            (MessageVersion.Soap12WSAddressing10,
                                                             action,
                                                             message.Parameter);
            //adicionar os header criados a mensagem
            foreach (MessageHeader header in headerList)
                msg.Headers.Add(header);

            return msg;
        }
    }
}
